using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace MyClever.Lib
{
    /// <summary>
    /// You can use this enum-flag for defining your relevant setting parameter type.
    /// So it is possible to ask for userDynamic or pluginDynamic settings only.
    /// </summary>
    [Flags]
    public enum SettingParameterType
    {
        /// <summary>
        /// By using this flag, you will get all user relevant setting-parameters.
        /// Typically this are setting-parameters you should ask the user for.
        /// You can define this parameter-type in your mission.xml-files by using 'Parameter Value="Dynamic.PluginXYZ"'.
        /// </summary>
        UserDynamic = 1,
        /// <summary>
        /// By using this flag, you will get all dynamic setting-parameters, which depends on another plugin-output.
        /// In fact all output-parameters you will get from a computed mission-plugin, you can use as a new
        /// setting-parameter for a another mission-plugin.
        /// You can define this parameter-type in your mission.xml-files by using 'Parameter Value="Dynamic.PluginXYZ.OutputParameterNameXYZ"'.
        /// </summary>
        PluginDynamic = 2,
        /// <summary>
        /// This flag process all setting-parameters which are static.
        /// You can set the static setting-parameter-value in you mission.xml-files by using 'Parameter Value="MyExpectedValue"'.
        /// </summary>
        NonDynamic = 4
    }
    
    public class MissionPluginParameter
    {
        private string value;
        private string validationType;

        #region Properties
        /// <summary>
        /// Gets the parameter value.
        /// </summary>
        /// <value>The value of the parameter.</value>
        public string Value
        {
            get { return this.value; }
        }

        /// <summary>
        /// Gets the validation-type of the parameter.
        /// You can set it to NULL, if you don't like any validation.
        /// </summary>
        /// <value>The validation-type of the parameter.</value>
        public string ValidationType
        {
            get { return this.validationType; }
        }
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="MissionParameter"/> class.
        /// </summary>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="value">The value of the parameter.</param>
        /// <param name="validation">The validation-type of the parameter.</param>
        public MissionPluginParameter(string value, string validationType)
        {
            this.value = value;
            this.validationType = validationType; //todo: make validation here?
        }

        /// <summary>
        /// Determines whether this parameter depends on another dynamic input.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this parameter is dynamic; otherwise, <c>false</c>.
        /// </returns>
        public bool IsDynamic()
        {
            return (this.Value.StartsWith("Dynamic."));
        }


        /// <summary>
        /// Determines whether this parameter depends on another plugin-output.
        /// </summary>
        /// <param name="dependentPluginName">Returns the name of the dependent plugin.
        /// If this parameter doesn't depending on a plugin, this reference returns 'null'.</param>
        /// <returns>
        /// 	<c>true</c> if this parameter depends on another plugin-output; otherwise, <c>false</c>.
        /// </returns>
        public bool IsPluginDynamic()
        {          
            // a parameter, which depends on another plugin, must be dynamic
            if (false == this.IsDynamic())
            {
                return false;
            }

            // very, very simple check!
            // a paramter which depends on another plugin has always the same syntax:
            // Dynamic.PluginXYZ.OutputParameterNameXYZ
            return (this.Value.Split('.').Length == 3);
        }

        /// <summary>
        /// Determines whether this missionParameter has a valid value.
        /// You can set the validationType in your mission.xml-files.
        /// </summary>
        /// <param name="loggingMessage">The logging message, which gives you more information about failure/success of the validation.</param>
        /// <returns>
        /// 	<c>true</c> if this parameter has a valid value; otherwise, <c>false</c>.
        ///     In case there is no validation is needed, you will get <c>true</c>.
        /// </returns>
        public bool IsValid(out string loggingMessage)
        {
            bool validateResult = true;
            loggingMessage = "no validation needed!";

            // should we do any validation?
            if (this.validationType != null)
            {
                // get all necessary validation arguments.
                List<string> validationArgs;
                string validationMethodName = this.GetValidationArguments(out validationArgs);

                Type validationType = Type.GetType("MyClever.Lib.Validation");
                MethodInfo validationMethodInfo = validationType.GetMethod(validationMethodName.ToLower());

                // start the validation by using .NET-Reflection
                try
                {
                    validateResult = (bool)validationMethodInfo.Invoke(validationType, validationArgs.ToArray());
                }
                catch (Exception exception)
                {
                    loggingMessage = exception.Message + "\n";
                    validateResult = false;
                }
                finally
                {
                    validationMethodInfo = null;

                    // create a logger message
                    loggingMessage = validationMethodName + "(";
                    foreach (string argOn in validationArgs)
                    {
                        loggingMessage += "\"" + argOn + "\",";
                    }
                    // delete the last commata
                    loggingMessage = loggingMessage.Substring(0, loggingMessage.Length - 1);
                    loggingMessage += ") returns:" + validateResult;
                }
            }
            return validateResult;
        }

        /// <summary>
        /// Gets all arguments the validation needed.
        /// You can set the validationType in your mission.xml-files by using 'ValidationType="ValditionTypeXYZ{ValidationArg1}"'.
        /// </summary>
        /// <param name="validationArgs">Holds all required validation arguments.</param>
        /// <returns>The name of the validation-method itself.</returns>
        private string GetValidationArguments(out List<string> validationArgs)
        {                    
            // extract all validation arguments.
            string[] validateMethodeElements = this.validationType.Split(new char[] { '{', '}' });
            string validationMethodeName = validateMethodeElements[0];
            
            // set all validate-methode arguments ...
            validationArgs = new List<string>();
            // ... 1) the setting-parameter value, which we like to validate
            validationArgs.Add(this.Value);
            // ... 2) all other arguments, which we need to validate 
            // (for example a string-character to comparing)
            for (int i = 1; i < validateMethodeElements.Length-1; i++)
            {
                validationArgs.Add(validateMethodeElements[i]);
            }

            return validationMethodeName; 
        }

        /// <summary>
        /// Sets a new value to this mission-parameter, if the value is valid.
        /// </summary>
        /// <param name="newValue">The new value you like to set.</param>
        /// <param name="loggingMessage">The logging message, , which gives you more information about failure/success of the validation.</param>
        /// <returns></returns>
        public bool SetValueIfValid(string newValue, out string loggingMessage)
        {
            // to be on the safe side, we save the old value ...
            string oldValue = this.value;
            // ... before we override them, with the new value 
            this.value = newValue;

            // is the new value valid?
            bool isValid = this.IsValid(out loggingMessage);

            if (false == isValid)
            {
                // we restore the old value 
                this.value = oldValue;
            }
            return isValid;
        }
    }
}
